package io.github.iogogogo.redis.util;

import com.fasterxml.jackson.core.type.TypeReference;
import io.github.iogogogo.commons.util.JsonParse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;

import java.util.concurrent.TimeUnit;

/**
 * Created by tao.zeng on 2024/11/17.
 */
@Slf4j
public class RedisTemplateUtils<K, V> {

    private final RedisTemplate<K, V> redisTemplate;

    public RedisTemplateUtils(RedisTemplate<K, V> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public RedisTemplate<K, V> redisTemplate() {
        return this.redisTemplate;
    }

    /**
     * 获取RedisTemplate的ValueOperations对象
     * 用于执行与值相关的操作，如设置、获取或删除键的值
     *
     * @return ValueOperations<K, V>对象，用于操作Redis中的值
     */
    public ValueOperations<K, V> valueOperations() {
        return redisTemplate.opsForValue();
    }

    /**
     * 获取用于操作Hash类型数据的HashOperations对象
     * 该方法提供了一种途径来获取HashOperations对象，用于执行涉及Hash类型数据的操作
     * Hash类型数据结构允许存储键值对集合，适用于需要在Redis中存储复杂数据结构的场景
     *
     * @return HashOperations<K, Object, Object> 返回一个HashOperations对象，用于执行Hash类型数据的操作
     */
    public HashOperations<K, Object, Object> hashOperations() {
        return redisTemplate.opsForHash();
    }

    /**
     * 获取Redis列表操作对象
     * <p>
     * 通过此方法，我们可以获得一个ListOperations对象，该对象提供了一系列针对Redis列表的操作方法
     * 这些操作方法使得对Redis列表的数据操作变得更为直观和简便
     *
     * @return ListOperations<K, V> 返回一个ListOperations对象，用于操作Redis列表
     */
    public ListOperations<K, V> listOperations() {
        return redisTemplate.opsForList();
    }

    /**
     * 获取Set操作的实例
     *
     * @return SetOperations<K, V> 实例，用于执行Set相关的操作
     */
    public SetOperations<K, V> setOperations() {
        return redisTemplate.opsForSet();
    }

    /**
     * 获取Redis中操作有序集合(ZSet)的工具类
     *
     * @param <K> ZSet中元素的键类型
     * @param <V> ZSet中元素的值类型
     * @return 返回一个ZSetOperations对象，用于执行有序集合相关操作
     */
    public ZSetOperations<K, V> zSetOperations() {
        return redisTemplate.opsForZSet();
    }

    /**
     * 获取用于操作HyperLogLog数据结构的操作器
     * HyperLogLog是一种概率数据结构，用于在大数据集上近似计算集合的基数
     * 该方法主要用于需要执行HyperLogLog相关操作的场景，如添加元素、计算基数等
     *
     * @return 返回一个HyperLogLogOperations接口的实现，用于操作HyperLogLog数据结构
     */
    public HyperLogLogOperations<K, V> hyperLogLogOperations() {
        return redisTemplate.opsForHyperLogLog();
    }

    /**
     * 获取Geo操作实例
     * <p>
     * 提供对Redis中Geo类型数据的操作支持，如添加、更新、查询地理位置信息等
     *
     * @return GeoOperations<K, V> 返回一个GeoOperations实例，用于执行Geo类型的数据操作
     */
    public GeoOperations<K, V> geoOperations() {
        return redisTemplate.opsForGeo();
    }

    /**
     * 获取Redis模板的流操作实例
     *
     * @return StreamOperations<K, Object, Object> 实例，用于执行流相关操作
     */
    public StreamOperations<K, Object, Object> streamOperations() {
        return redisTemplate.opsForStream();
    }

    /**
     * 设置给定键的过期时间
     * 此方法简化了expire方法的调用，直接使用秒作为时间单位
     *
     * @param key     要设置过期时间的键
     * @param timeout 过期时间，以秒为单位
     * @return 如果设置过期时间成功，则返回true；否则返回false
     */
    public boolean expire(K key, long timeout) {
        return Boolean.TRUE.equals(expire(key, timeout, TimeUnit.SECONDS));
    }

    /**
     * 设置缓存键的过期时间
     *
     * @param key      缓存键
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return 如果过期时间设置成功，返回true；否则返回false
     */
    public boolean expire(K key, long timeout, TimeUnit timeUnit) {
        // 使用Redis模板的expire方法来设置键的过期时间，如果设置成功，返回true
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, timeUnit));
    }

    /**
     * 判断指定的键是否存在于Redis中
     *
     * @param key 要检查的键
     * @return 如果键存在，则返回true；否则返回false
     */
    public boolean exists(K key) {
        // 使用redisTemplate的hasKey方法检查键是否存在，该方法返回Boolean对象，这里与Boolean.TRUE比较
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    /**
     * 删除指定的键
     *
     * @param key 要删除的键
     * @return 如果删除成功返回true，否则返回false
     */
    public boolean delete(K key) {
        // 使用redisTemplate的delete方法删除指定的键，并将返回值与Boolean.TRUE进行比较 以确定键是否删除成功
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    public V get(String key) {
        return valueOperations().get(key);
    }

    public <T> T get(String key, Class<T> clz) {
        return JsonParse.tryParse((String) get(key), clz);
    }

    public <T> T get(String key, TypeReference<T> type) {
        return JsonParse.tryParse((String) get(key), type);
    }

    public void set(K key, V value) {
        set(key, value, -1);
    }

    public void set(K key, V value, int timeout) {
        set(key, value, timeout, TimeUnit.SECONDS);
    }

    public void set(K key, V value, int timeout, TimeUnit timeUnit) {
        valueOperations().set(key, value);
        if (timeout > 0)
            redisTemplate.expire(key, timeout, timeUnit);
    }
}
